<?php
/**
 * Created by PhpStorm.
 * User: aDaddy
 * Date: 2016/7/25
 * Time: 11:28
 */

namespace App\Base;

use Doctrine\Bundle\DoctrineBundle\Registry;
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityManager;
use Doctrine\Persistence\ManagerRegistry;


class BaseModel {

    /**
     * @var EntityManager
     */
    public $em ;

    /**
     * @var Registry
     */
    private $doctrine;

    public function __construct(ManagerRegistry $thisGetDoctrine )
    {
        $this->doctrine = $thisGetDoctrine;
        $this->em = $this->doctrine->getManager("default");
    }

    /**
     * @return Registry
     */
    public function getDoctrine()
    {
        return $this->doctrine;
    }

    public function getQueryBuilder($conn = "")
    {
        if (!$conn) {
            $conn = $this->myEm()->getConnection();
        }
        return new QueryBuilder($this->myEm());
    }

    /**
     * @return EntityManager
     */
    public function myEm()
    {
        return $this->getDoctrine()->getManager();
    }

    public function myRep($name)
    {
        return $this->myEm()->getRepository($name);
    }

    public function myRst($em){

    }

    public function builderCatTreeObj($data)
    {
        $newData = array();
        foreach ($data as $d) {//根据parent取出子项
            $newData[$d->getId()] = $d;
        }
        $groupByParent = array();
        foreach ($newData as $d) {//根据parent取出子项
            $groupByParent[$d->getParent()][$d->getSort()*10000 + $d->getId()] = $d;
        }

        $curParent = 0;
        $isEnd = 0;
        $sortedArray = array();
        $i = 0;
        if (isset($groupByParent[$curParent])) {
            do {
                ksort($groupByParent[$curParent]);
                if (isset($groupByParent[$curParent])) {//此ID下有分类
                    //设置这个父元素的相邻父元素
                    $first = reset($groupByParent[$curParent]);
                    //取出当前分组的第一个元素并删除
                    $sortedArray[] = $first;
                    array_shift($groupByParent[$curParent]);
                    if (isset($groupByParent[$first->getId()])) {//查找子元素
                        $curParent = $first->getId();
                    }else{//子元素没有的时候需要检测时候还有兄弟元素
                        while(empty($groupByParent[$curParent])) {
                            unset($groupByParent[$curParent]);
                            if ($curParent == 0) {
                                $isEnd = 1;
                                break;
                            }
                            $curParent = $newData[$curParent]->getParent();
                        }
                    }
                    if ($isEnd) {break;}
                }
                $i ++ ;
            } while (count($sortedArray) < count($newData) && $i < 100);
        } else {
            $sortedArray = $newData;
        }
        return $sortedArray;
    }


    public function buildeCatTreeArray($data)
    {
        $newData = array();
        foreach ($data as $d) {//根据parent取出子项
            $newData[$d["id"]] = $d;
        }
        $groupByParent = array();
        foreach ($newData as $d) {//根据parent取出子项
            $groupByParent[$d["parent"]][$d["sort"]*1000000 + $d["id"]] = $d;
        }

        $curParent = 0;
        $isEnd = 0;
        $sortedArray = array();
        $i = 0;
        if (isset($groupByParent[$curParent])) {
            do {
                ksort($groupByParent[$curParent]);
                if (isset($groupByParent[$curParent])) {//此ID下有分类
                    //设置这个父元素的相邻父元素
                    $first = reset($groupByParent[$curParent]);
                    //取出当前分组的第一个元素并删除
                    $sortedArray[] = $first;
                    array_shift($groupByParent[$curParent]);
                    if (isset($groupByParent[$first["id"]])) {//查找子元素
                        $curParent = $first["id"];
                    }else{//子元素没有的时候需要检测时候还有兄弟元素
                        while(empty($groupByParent[$curParent])) {
                            unset($groupByParent[$curParent]);
                            if ($curParent == 0) {
                                $isEnd = 1;
                                break;
                            }
                            $curParent = $newData[$curParent]["parent"];
                        }
                    }
                    if ($isEnd) {break;}
                }
                $i ++ ;
            } while (count($sortedArray) < count($newData) && $i < 100);
        } else {
            $sortedArray = $newData;
        }
        return $sortedArray;
    }

    //**************************************列表条件筛选***********************************************
    //关键词搜索
    /**
     * @param QueryBuilder $qb
     * @param array $listSettings   列表项配置
     * @param string $textKey       文本关键词
     * @param int $type             整体条件AND/OR类型，type：[默认]0=AND，1=OR
     * @return QueryBuilder
     */
    public function search(QueryBuilder &$qb , $listSettings , $textKey , $type = 0)
    {
        $search = $qb->expr()->orX();
        foreach ($listSettings as $setting) {
            foreach ($setting as $key => $val) {
                if (stripos($key,"AS") > 0){
                    $data = mb_split("AS",$key);
                    $key = trim($data[1]);
                }
                $searchKeys = $val["search"];
                foreach ($searchKeys as $k) {
                    $search->add($qb->expr()->like($key.".".$k,":searchText"));
                }
            }
        }
        if ($type) {
            $qb->orWhere($search)->setParameter("searchText"   , $textKey);
        } else {
            $qb->andWhere($search)->setParameter("searchText"   , $textKey);
        }
        return $qb;
    }

    //条件检索
    /**
     * @param QueryBuilder $qb
     * @param array $listSettings   列表项配置
     * @param array $conditions     例：Array[Entity][ConditionKey][ConditionType] = ConditionValue
     * @param int $type             整体条件AND/OR类型，type：[默认]0=AND，1=OR
     * @param int $sonType          子条件AND/OR类型，type：[默认]0=AND，1=OR
     * @return QueryBuilder
     */
    public function condition(QueryBuilder &$qb , $listSettings , $conditions , $type = 0 , $sonType = 0)
    {
        if ($sonType) {
            $cond = $qb->expr()->orX();
        } else {
            $cond = $qb->expr()->andX();
        }
        $params = array();
        $atListOneCondition = 0;
        foreach ($listSettings as $setting) {
            foreach ($setting as $key => $val) {
//                $confKey = $key;
                if (stripos($key,"AS") > 0){
                    $data = mb_split("AS",$key);
                    $table = trim($data[0]);
                    $key = trim($data[1]);
                }else{
                    $table = $key;
                }
                $conditionKeys = $val["condition"];
                if (!isset($conditions[$key])) {
                    continue;
                }
                foreach ($conditions[$key] as $dbKey => $dbVal) {
                    foreach ($dbVal as $ConditionType => $ConditionValue) {
                        if ($key == "SarsUser" && $ConditionType == "eq") {

                            $ConditionType = "like";
//                        $ConditionType =array_keys($dbVal)[0];
//                        $ConditionValue = array_values($dbVal)[0];
                            if (in_array($dbKey,$conditionKeys)) {
                                $atListOneCondition += 1;
                                $cond->add($qb->expr()->$ConditionType($key.".".$dbKey,":$key$dbKey"."_"."$ConditionType"));
                                $params["$key$dbKey"."_"."$ConditionType"] = "%{$ConditionValue}%";
                            }
                        }else{
//                        $ConditionType =array_keys($dbVal)[0];
//                        $ConditionValue = array_values($dbVal)[0];
                            if (in_array($dbKey,$conditionKeys)) {
                                $atListOneCondition += 1;
                                $cond->add($qb->expr()->$ConditionType($key.".".$dbKey,":$key$dbKey"."_"."$ConditionType"));
                                $params["$key$dbKey"."_"."$ConditionType"] = "{$ConditionValue}";
                            }
                        }
                    }
                }
            }
        }
        if ($atListOneCondition) {

            if ($type) {
                $qb->orWhere($cond);
            } else {
                $qb->andWhere($cond);
            }
            foreach ($params as $k=>$v) {
                $qb->setParameter($k,$v);
            }
        }
        return $qb;
    }

    //表关联
    /**
     * @param QueryBuilder $qb
     * @param $listSettings
     * @return QueryBuilder
     */
    public function joins(QueryBuilder &$qb , $listSettings )
    {
//        return $qb;
        $from = "";
        foreach ($listSettings["default"] as $fromKey => $v) {
            $from = $fromKey;
        }
        if (stripos($from,"AS") > 0){
            $data = mb_split("AS",$from);
            $from = trim($data[1]);
        }
        foreach ($listSettings["joins"] as $entityName => $setting) {
//            if (stripos($entityName,"AS") > 0){
//                $data = mb_split("AS",$entityName);
//                $entityName = trim($data[0]);
//                $setting["as"] = trim($data[1]);
//            }
            $condition = $setting["on"];
//            $as = isset($setting["as"])?$setting["as"]:$entityName;
            $qb->join("App:".$entityName,$entityName,"with",$condition);
        }
        return $qb;
    }

    /**
     * @param QueryBuilder $qb
     * @param $listSettings
     * @return QueryBuilder
     */
    public function selectFrom(QueryBuilder &$qb , $listSettings)
    {
        $from = "";
        foreach ($listSettings["default"] as $fromKey => $v) {
            $from = $fromKey;
        }
        if (stripos($from,"AS") > 0){
            $data = mb_split("AS",$from);
            $qb->from(trim($data[0]),trim($data[1]));
        }else{
            $qb->from("App:".$from,$from);
        }
        $qb->select("$from");
        $selectStr = "";
        foreach ($listSettings as $type=>$setting) {
            if ($type == "joins") {
                foreach ($setting as $key => $val) {
                    if (stripos($key,"AS") > 0){
                        $data = mb_split("AS",$key);
                        $key = trim($data[1]);
                    }


                    $selectKeys = $val["select"];
                    foreach ($selectKeys as $dbKey) {
                        if (strpos($dbKey,"(")>0) {
                            $fieldName = str_replace("(","(".$key.".",$dbKey);
                            preg_match("/\((.+)\)/",$dbKey,$matches);
                            $aliasName = $key."_".$matches[1];
                        } else {
                            $fieldName = $key.".".$dbKey;
                            $aliasName = $key."_".$dbKey;
                        }
                        $selectStr .= $selectStr ? " , $fieldName as $aliasName" : " $fieldName as $aliasName";
                        $qb->addSelect($fieldName);
                    }
                }
            }
        }
//        $qb->select($selectStr);
        return $qb;
    }

    /**
     * @param QueryBuilder $qb
     * @param $listSettings
     * @param $orders
     */
    public function orderBy(QueryBuilder &$qb , $listSettings , $orders)
    {
//        var_dump($orders);
//        $keys = array();
        $newKeys = array();
//        foreach ($orders as $entityName => $orderBys) {
//            foreach ($orderBys as $orderKey => $order) {
//                $keys[] = $orderKey;
//            }
//        }
        if (!$orders) {
            $newOrders = [];
            foreach ($listSettings as $setting) {
                foreach ($setting as $entityName => $val) {
                    $orderKeys = $val["orderBy"];
                    foreach ($orderKeys as $dbKey=>$order) {
//                    if (in_array($dbKey,$keys)) {
//                        $newKeys[] = $dbKey;
//                    }
                        if (is_numeric($dbKey)) {//默认DESC
                            $newOrders[$entityName][$order]="ASC";
                        } else {
                            $newOrders[$entityName][$dbKey]=$order;
                        }

                    }
                }
            }
            $orders = $newOrders;
        }

//        var_dump($newOrders);
//        var_dump($orders);
        foreach ($orders as $entityName => $orderBys) {
            foreach ($orderBys as $orderKey => $order) {
//                if (in_array($orderKey,$newKeys) || 1) {
                    $key = $entityName;
                    if (stripos($key,"AS") > 0){
                        $data = mb_split("AS",$key);
                        $key = trim($data[1]);
                    }
//                    var_dump($key);
//                    var_dump($orderKey);
//                    var_dump($order);
                    $qb->addOrderBy("$key"."."."$orderKey",$order);
//                }
            }
        }
    }

    /**
     * @param QueryBuilder $qb
     * @param $groupBy
     */
    public function groupBy(QueryBuilder &$qb , $groupBy)
    {
        return;
        foreach ($groupBy as $entityName => $fields) {
            foreach ($fields as $field) {
                $qb->groupBy($entityName.".".$field);
            }
        }
    }


    /**
     * @param array $params $param[textKey]=textKey , $param[conditions][table][field][eq|gt|lt...]=value , $param[orderBy][table][field]=DESC|ASC  , $param[groupBy][table]=array(field1,field2) ,
     * @return QueryBuilder
     */
    public function getQueryFromParam($params)
    {
        $textKey = isset($params["textKey"]) ? $params["textKey"] : false;
        $conditions = isset($params["conditions"]) ? $params["conditions"] :array();
        $orderBy = isset($params["orderBy"]) ? $params["orderBy"] :array();
        $groupBy = isset($params["groupBy"]) ? $params["groupBy"] :array();

        $listSettings = $this->listSettingDefault();
//        $qb = $this->getQueryBuilder();
        $qb = $this->createDql();

        $this->selectFrom($qb,$listSettings);
        $this->joins($qb,$listSettings);
        if ($textKey !== false) {
            $this->search($qb,$listSettings,$textKey);
        }
        if (!empty($conditions)) {
            $this->condition($qb,$listSettings,$conditions);
        }
        if (!empty($orderBy) || 1) {
            $this->orderBy($qb,$listSettings,$orderBy);
        }
        if (!empty($groupBy)) {
            $this->groupBy($qb,$groupBy);
        }
        return $qb;
    }

    /**
     * @param QueryBuilder $queryBuilder
     * @param $offset
     * @param $perPage
     * @return array
     */
    public function getAllResult(QueryBuilder $queryBuilder ,$offset = 0 , $perPage = 0 )
    {
        if ($offset == 0 && $perPage == 0) {

        }else{
            $queryBuilder->setFirstResult($offset);
            $queryBuilder->setMaxResults($perPage);
        }
        return $queryBuilder->getQuery()->execute();
//        return $queryBuilder->execute()->fetchAll();
    }

    /**
     * @param QueryBuilder $queryBuilder
     * @return int
     */
    public function countAllResult(QueryBuilder $queryBuilder)
    {
        $queryBuilder->setFirstResult(0);
        $queryBuilder->setMaxResults(1530494977);
//        $queryBuilder->select("count(". $this->model.".id)");
//        $queryBuilder->orderBy( $this->model.".id");
//        $sql = $queryBuilder->getQuery()->getSQL();
//        var_dump($sql);die;
        $data = $queryBuilder->getQuery()->getArrayResult();
//        var_dump($data);
        return count($data);
//        return $queryBuilder->execute()->rowCount();
    }


    /*
     * 为后台获取所有列表
     */
    /**
     * @param array $params $param[textKey]=textKey , $param[conditions][table][field][eq|gt|lt...]=value , $param[orderBy][table][field]=DESC|ASC
     * @param int $offset
     * @param int $perPage
     * @return array
     */
    public function getAdminLists($params ,$offset = 0 , $perPage = 10)
    {
        $qb = $this->getQueryFromParam($params);
        $lists = $this->getAllResult($qb,$offset,$perPage);
        $total = $this->countAllResult($qb);

        return array(
            "total" => $total ? $total : 0,
            "data"  => $lists
        );
    }

    /**
     * @param $entity
     * @return array
     */
    public function toArray(&$entity)
    {
        if(!$entity) return[];
        $methods = (get_class_methods(get_class($entity)));
        $arrData = array();
        foreach ($methods as $method) {
            $attr = lcfirst(substr($method,3));
            $funcName = "get".ucfirst($attr);
            $arrData[$attr] = $entity->$funcName();
        }
        $entity = $arrData;
        return $arrData;
    }


    /**
     * @param array $old
     * @param array $new
     * @param string $prefix
     * @return mixed
     */
    public function concat($old,$new,$prefix = ""){
        foreach ($new as $key => $value) {
            $old[$prefix.ucfirst($key)] = $value;
        }
        return $old;
    }

    /**
     * 计算两组经纬度坐标 之间的距离;params ：lat1 ； lng1 经度1； lat2 纬度2； lng2 经度2；
     * @param $lat1
     * @param $lng1
     * @param $lat2
     * @param $lng2
     * @return float
     */
    function getDistance($lat1, $lng1, $lat2, $lng2)
    {
        $EARTH_RADIUS = 6378.137;
        $PI = 3.1415926;
        $decimal = 2;// decimal小数点后几位
        $len_type = 2;// len_type长度单位 （1:m or 2:km);
        $radLat1 = $lat1 * $PI / 180.0;
        $radLat2 = $lat2 * $PI / 180.0;
        $a = $radLat1 - $radLat2;
        $b = ($lng1 * $PI / 180.0) - ($lng2 * $PI / 180.0);
        $s = 2 * asin(sqrt(pow(sin($a/2),2) + cos($radLat1) * cos($radLat2) * pow(sin($b/2),2)));
        $s = $s * $EARTH_RADIUS;
        $s = round($s * 1000);
        if ($len_type > 1)
        {
            $s /= 1000;
        }
        return round($s, $decimal);
    }


    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getWeekStart($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return $time - ((date('w') == 0 ? 7 : date('w')) - 1) * 24 * 3600;
    }

    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getWeekEnd($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return $time + (7 - (date('w') == 0 ? 7 : date('w'))) * 24 * 3600;
    }



    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getMonthStart($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return strtotime(date('Y-m', $time) . '-01 00:00:00');
    }

    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getMonthEnd($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return strtotime(date('Y-m', $time) . '-' . date('t', time()) . ' 00:00:00');
    }

    public function myExplodeNumbers($string)
    {
        preg_match_all("/[0-9\.?]+/",$string,$array);
        return $array[0];
    }


    /**
     * @param string $time
     * @return \DateTime
     */
    public function getDateTime($time = "")
    {
        if (!$time && $time !== 0) {
            $time = time();
        }
        $DateTime = new \DateTime();
        $DateTime->format("Y-m-d H:i:s");
        $DateTime->setTimestamp($time);
        return $DateTime;
    }


    public function createOrderSn($uid = "")
    {
//        return date(YMDHIS).rand(1000,9999).$uid;
        return date(YMDHIS).rand(0,9).chr(rand(65,90)).$uid;
    }

    public function createPayCalBackCode($uid,$type="wx")
    {
        return md5($type.uniqid($uid).BaseConfig::__VI__.rand(1000,9999));
    }


    public function arrayValueToString(&$arr)
    {
        foreach ($arr as &$val) {
            if (is_array($val)) {
                $this->arrayValueToString($val);
            }else{
                $val .= "";
            }
        }
    }


    /**
     * @return \Doctrine\ORM\QueryBuilder
     */
    public function createDql()
    {
        return $this->em->createQueryBuilder();
    }


    public function excSql($sql)
    {
        $query = $this->em->getConnection()->prepare($sql);
        $query->execute();
        return $query->fetchAll();
    }


    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getDayStart($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return strtotime(date( "Y-m-d" , $time ));
    }

    /**
     * @param string $timeOrDate
     * @return int
     */
    public function getDayEnd($timeOrDate = "")
    {
        if (!$timeOrDate) {
            $timeOrDate = time();
        }
        if (is_string($timeOrDate)) {
            $time = strtotime($timeOrDate);
        } else {
            $time = (int) $timeOrDate;
        }
        return strtotime(date( "Y-m-d" , $time )) + 24 * 3600 - 1;
    }


    public function timeToYMDHIS($time = 0,$pos = "",$isEnd = 0)
    {
        if (!$pos) {
            if (!$time) {
                $time = time();
            }
            return date("Y-m-d H:i:s",$time);
        }
        $isEnd = 1;
        if ($isEnd) {
            switch ($pos) {
                case "Y":
                    $day = $time / (3600 * 24 * 365);
                    return floor($day);
                case "m":
                    $day = $time / (3600 * 24 * 30);
                    return floor($day);
                case "d":
                    $day = $time / (3600 * 24);
                    return floor($day);
                case "H":
                    $day = $time / (3600);
                    return floor($day);
                case "i":
                    $day = $time / (60);
                    return floor($day);
            }
        }else{
            return (int)date("$pos",$time);
        }

    }

    public function createOption4Select($data,$entity,$key="name")
    {
        $choices = [];

        $func = "get".ucfirst($key);
        foreach ($data as $row) {
            $choices["/api_/$entity/".$row->getId()] = $row->$func();
        }
        return $choices;
        return ["other"=>["choices"=>array_flip($data)]];
    }
    public function createOption4Radio($data)
    {
        return ["other"=>["choices"=>($data)]];
    }

    public function createSelectFromCategoryTree($data)
    {
        $re = [];
        foreach ($data as $item) {
            $id = $item["id"];
            $re["[$id]".$item["name"]] = $item["id"];
        }
        return $re;
    }

    public function createSelectFromEntity($data,$key = "name")
    {
        $func = "get".ucfirst($key);
        $re = [];
        foreach ($data as $item) {

            $re[$item->$func()] = $item->getId();
        }
        return $re;
    }

    public function createListCondition4SelectFromEntity($data,$key = "name")
    {
        $re = [];
        $func = "get".ucfirst($key);
        foreach ($data as $row) {
           $re[]=[
               "id"=>$row->getId(),
               "name"=>$row->$func(),
           ];
        }
        return $re;
    }
}
